#include <geant4/globals.hh>
#include "PhysicsList.hh"

#include <geant4/G4ProcessManager.hh>
#include <geant4/G4ParticleTypes.hh>

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

PhysicsList::PhysicsList():  G4VUserPhysicsList()
{
  defaultCutValue = 0.01*mm;
   SetVerboseLevel(1);
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

PhysicsList::~PhysicsList()
{}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PhysicsList::ConstructParticle()
{
  // In this method, static member functions should be called
  // for all particles which you want to use.
  // This ensures that objects of these particle types will be
  // created in the program. 

  ConstructBosons();
  ConstructLeptons();
  ConstructOpticalPhotons();
//  ConstructMesons();
//  ConstructBaryons();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PhysicsList::ConstructBosons()
{
//  // pseudo-particles
//  G4Geantino::GeantinoDefinition();
//  G4ChargedGeantino::ChargedGeantinoDefinition();

  // gamma
  G4Gamma::GammaDefinition();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PhysicsList::ConstructLeptons()
{
  // leptons
  //  e+/-
  G4Electron::ElectronDefinition();
  G4Positron::PositronDefinition();
}

void PhysicsList::ConstructOpticalPhotons()
{
  G4OpticalPhoton::OpticalPhotonDefinition();
}


void PhysicsList::ConstructProcess()
{
  AddTransportation();
  ConstructEM();
  ConstructOptical();
  ConstructGeneral();
  AddStepMax();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

#include <geant4/G4ComptonScattering.hh>
#include <geant4/G4GammaConversion.hh>
#include <geant4/G4PhotoElectricEffect.hh>

#include <geant4/G4eMultipleScattering.hh>
#include <geant4/G4hMultipleScattering.hh>

#include <geant4/G4eIonisation.hh>
#include <geant4/G4eBremsstrahlung.hh>
#include <geant4/G4eplusAnnihilation.hh>

#include <geant4/G4MuIonisation.hh>
#include <geant4/G4MuBremsstrahlung.hh>
#include <geant4/G4MuPairProduction.hh>

#include <geant4/G4hIonisation.hh>
#include <geant4/G4hBremsstrahlung.hh>
#include <geant4/G4hPairProduction.hh>

#include <geant4/G4ionIonisation.hh>

#include "Scintillation.h"
#include <geant4/G4Cerenkov.hh>
#include <geant4/G4OpAbsorption.hh>
#include <geant4/G4OpRayleigh.hh>
#include <geant4/G4OpBoundaryProcess.hh>
#include <geant4/G4OpWLS.hh>

#include <geant4/G4LossTableManager.hh>

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PhysicsList::ConstructEM()
{
  theParticleIterator->reset();
  while( (*theParticleIterator)() ){
    G4ParticleDefinition* particle = theParticleIterator->value();
    G4ProcessManager* pmanager = particle->GetProcessManager();
    G4String particleName = particle->GetParticleName();
     
    if (particleName == "gamma") {
      // gamma         
      pmanager->AddDiscreteProcess(new G4PhotoElectricEffect);
      pmanager->AddDiscreteProcess(new G4ComptonScattering);
      pmanager->AddDiscreteProcess(new G4GammaConversion);
      
    } else if (particleName == "e-") {
      //electron
      pmanager->AddProcess(new G4eMultipleScattering, -1, 1, 1);
      pmanager->AddProcess(new G4eIonisation,         -1, 2, 2);
      pmanager->AddProcess(new G4eBremsstrahlung,     -1, 3, 3);      

    } else if (particleName == "e+") {
      //positron
      pmanager->AddProcess(new G4eMultipleScattering, -1, 1, 1);
      pmanager->AddProcess(new G4eIonisation,         -1, 2, 2);
      pmanager->AddProcess(new G4eBremsstrahlung,     -1, 3, 3);
      pmanager->AddProcess(new G4eplusAnnihilation,    0,-1, 4);

    }
  }
}

void PhysicsList::ConstructOptical()
{
  Scintillation* theScintProcess;
  G4Cerenkov* theCerenkovProcess;
  G4OpAbsorption* theAbsorptionProcess;
  //G4OpRayleigh* theRayleighScattering;
  G4OpBoundaryProcess* theBoundaryProcess;

  theAbsorptionProcess=new G4OpAbsorption();
  //theRayleighScattering=new G4OpRayleigh();

  theBoundaryProcess=new G4OpBoundaryProcess();
  // theBoundaryProcess->SetModel(unified); // moved to G4OpticalSurface since geant4.9.6

  G4ProcessManager * pManager = 0;

  pManager = G4OpticalPhoton::OpticalPhoton()->GetProcessManager();
  pManager->AddDiscreteProcess(theAbsorptionProcess);
  //pManager->AddDiscreteProcess(theRayleighScattering);
  pManager->AddDiscreteProcess(theBoundaryProcess);

  theScintProcess = new Scintillation();
  theScintProcess->SetScintillationYieldFactor(1.);
  //theScintProcess->SetScintillationExcitationRatio(0.0);
  theScintProcess->SetTrackSecondariesFirst(true);

  // Use Birks Correction in the Scintillation process

  //G4EmSaturation* emSaturation = G4LossTableManager::Instance()->EmSaturation();
  //theScintProcess->AddSaturation(emSaturation);

  theCerenkovProcess=new G4Cerenkov("Cerenkov");
  theCerenkovProcess->SetMaxNumPhotonsPerStep(300);
  theCerenkovProcess->SetMaxBetaChangePerStep(10.0);
  theCerenkovProcess->SetTrackSecondariesFirst(true);

  theParticleIterator->reset();
  while( (*theParticleIterator)() ){
    G4ParticleDefinition* particle = theParticleIterator->value();
    pManager = particle->GetProcessManager();
    if(theCerenkovProcess->IsApplicable(*particle)){
      pManager->AddProcess(theCerenkovProcess);
      pManager->SetProcessOrdering(theCerenkovProcess,idxPostStep);
    }
    if(theScintProcess->IsApplicable(*particle)){
      pManager->AddProcess(theScintProcess);
      pManager->SetProcessOrderingToLast(theScintProcess,idxAtRest);
      pManager->SetProcessOrderingToLast(theScintProcess,idxPostStep);
    }

  }
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

#include <geant4/G4Decay.hh>

void PhysicsList::ConstructGeneral()
{
  // Add Decay Process
  G4Decay* theDecayProcess = new G4Decay();
  theParticleIterator->reset();
  while( (*theParticleIterator)() ){
    G4ParticleDefinition* particle = theParticleIterator->value();
    G4ProcessManager* pmanager = particle->GetProcessManager();
    if (theDecayProcess->IsApplicable(*particle)) { 
      pmanager ->AddProcess(theDecayProcess);
      // set ordering for PostStepDoIt and AtRestDoIt
      pmanager ->SetProcessOrdering(theDecayProcess, idxPostStep);
      pmanager ->SetProcessOrdering(theDecayProcess, idxAtRest);
    }
  }
}
  
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

#include <geant4/G4StepLimiter.hh>
#include <geant4/G4UserSpecialCuts.hh>

void PhysicsList::AddStepMax()
{
  // Step limitation seen as a process
  G4StepLimiter* stepLimiter = new G4StepLimiter();
  G4UserSpecialCuts* userCuts = new G4UserSpecialCuts();
  
  theParticleIterator->reset();
  while ((*theParticleIterator)()){
      G4ParticleDefinition* particle = theParticleIterator->value();
      G4ProcessManager* pmanager = particle->GetProcessManager();

      if (particle->GetPDGCharge() != 0.0)
        {
	  pmanager ->AddDiscreteProcess(stepLimiter);
          pmanager ->AddDiscreteProcess(userCuts);
        }
  }
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PhysicsList::SetCuts()
{
  //G4VUserPhysicsList::SetCutsWithDefault method sets 
  //the default cut value for all particle types 
  //
  SetCutsWithDefault();
     
  if (verboseLevel>0) DumpCutValuesTable();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

